using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using Microsoft.Win32.SafeHandles;

namespace Gko.Utils
{
	/// <summary>
	/// Mapped view of file.
	/// </summary>
	[CLSCompliant(false)]
	public class FileMappingView : DisposableBase
	{
		private readonly FileMappingAccess access;
		private readonly SafeFileHandle handle;
		private readonly uint size;
		private IntPtr pointer;

		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2001:AvoidCallingProblematicMethods", MessageId = "System.Runtime.InteropServices.SafeHandle.DangerousGetHandle")]
		internal FileMappingView(SafeFileHandle handle, FileMappingAccess access, ulong offset, uint size)
		{
			this.handle = handle;
			this.access = access;
			this.size = size;
			this.Pointer = NativeMethods.MapViewOfFile(this.handle.DangerousGetHandle(), (uint)access, (uint)(offset >> 32), (uint)(offset & 0xFFFFFFFF), size);
			if (this.Pointer != IntPtr.Zero) {
				return;
			}
			var hResult = Marshal.GetHRForLastWin32Error();
			Marshal.ThrowExceptionForHR(hResult);
		}

		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2001:AvoidCallingProblematicMethods", MessageId = "System.Runtime.InteropServices.SafeHandle.DangerousGetHandle")]
		internal FileMappingView(SafeFileHandle handle, FileMappingAccess access, ulong offset, uint size, IntPtr baseAddress)
		{
			this.handle = handle;
			this.access = access;
			this.size = size;
			this.Pointer = NativeMethods.MapViewOfFileEx(this.handle.DangerousGetHandle(), (uint)access, (uint)(offset >> 32), (uint)(offset & 0xFFFFFFFF), size, baseAddress);
			if (this.Pointer != IntPtr.Zero) {
				return;
			}
			var hResult = Marshal.GetHRForLastWin32Error();
			Marshal.ThrowExceptionForHR(hResult);
		}

		/// <summary>
		/// Gets the pointer to the starting address of the mapped view.
		/// </summary>
		/// <value>The pointer.</value>
		/// <exception cref="System.ObjectDisposedException">The instance is disposed.</exception>
		public IntPtr Pointer
		{
			get
			{
				this.VerifyNotDisposed();
				return this.pointer;
			}
			private set
			{
				this.VerifyNotDisposed();
				this.pointer = value;
			}
		}

		/// <summary>
		/// Checks whether the specified address is within a memory-mapped file in the address space of the specified process. If so, the function returns the name of the memory-mapped file.
		/// </summary>
		/// <value>The name of the memory-mapped file to which this view belongs.</value>
		/// <exception cref="System.ObjectDisposedException">The instance is disposed.</exception>
		public string FileName
		{
			get
			{
				this.VerifyNotDisposed();
				var sb = new StringBuilder(260);
				var length = NativeMethods.GetMappedFileNameW(NativeMethods.GetCurrentProcess(), this.Pointer, sb, (uint)sb.Capacity);
				if (length == 0) {
					var hResult = Marshal.GetHRForLastWin32Error();
					int completedSuccefuly;
					unchecked {
						completedSuccefuly = (int)0x80070000u;
					}
					if (hResult == completedSuccefuly) {
						return sb.ToString();
					}
					Marshal.ThrowExceptionForHR(hResult);
				}
				return sb.ToString(0, (int)length);
			}
		}

		/// <summary>
		/// Creates the stream.
		/// </summary>
		/// <returns>Stream</returns>
		public Stream CreateStream()
		{
			return new UnmanagedMemoryStream(this.pointer, this.size, ((this.access | FileMappingAccess.Read) != 0), ((this.access | FileMappingAccess.Write) != 0));
		}

		/// <summary>
		/// Writes to the disk a byte range within a mapped view of a file.
		/// </summary>
		/// <exception cref="System.ObjectDisposedException">The instance is disposed.</exception>
		public void Flush()
		{
			this.Flush(0u);
		}

		/// <summary>
		/// Writes to the disk a byte range within a mapped view of a file.
		/// </summary>
		/// <param name="numberOfBytesToFlush">The number of bytes to be flushed. If size is zero, the file is flushed from the base address to the end of the mapping.</param>
		/// <exception cref="System.ObjectDisposedException">The instance is disposed.</exception>
		public void Flush(uint numberOfBytesToFlush)
		{
			this.VerifyNotDisposed();
			if (NativeMethods.FlushViewOfFile(this.Pointer, numberOfBytesToFlush)) {
				return;
			}
			var hResult = Marshal.GetHRForLastWin32Error();
			Marshal.ThrowExceptionForHR(hResult);
		}

		/// <summary>
		/// Closes this view.
		/// </summary>
		public void Close()
		{
			this.Dispose();
		}

		protected override void OnDisposeUnmanaged()
		{
			base.OnDisposeUnmanaged();
			NativeMethods.UnmapViewOfFile(this.pointer);
		}
	}
}